/** Archivo de implementación de las funciones de ksamp.h **/

#include "ksamp.h"

status_t open_file (const char* path,FILE ** fd) { 
	
	*fd = fopen(path,"r") ; 
	if ( *fd == NULL ) { 
		return ERR_CAN_NOT_OPEN_FILE ; 
	}
	return OK ; 

}

void parse_colon ( char* info,  char** str ) { 
	*str = strpbrk(info,":") ; 			  	
	*str += 2*sizeof(char) ;  
} 

void remove_newline (char* str) { 
	str[strlen(str)-1] = '\0' ; 
}

status_t print_cpuinfo () {

	status_t st ;
	FILE *  fd ;
	char *  aux ; 
	char *  p ; 
	int 	i ;

	aux = (char *) malloc ( sizeof(char)*MAX_STR ) ; 

	if (  (st=open_file("/proc/cpuinfo",&fd)) != OK ) { 
		return st ; 
	} 

	for ( i = 0 ; i < 5 ; i++ ) {
		
		fgets( aux,MAX_STR, fd ) ;
		remove_newline(aux) ; 
		if ( i == 1 ) { 
			parse_colon(aux,&p) ; 
			fprintf(stdout,"Type: %s \n",p) ; 
		} 
		if ( i == 4 ) { 
			parse_colon(aux,&p) ; 
			fprintf(stdout,"Model: %s \n",p) ; 
		}
	} 
	if (fclose(fd) == EOF) { 
		return ERR_CAN_NOT_CLOSE_FILE ; 
	} 

	free(aux) ;
	return OK ;
}

status_t print_kernel() { 
	status_t st ; 
	FILE * fd ; 
	char * aux ; 

	aux = (char *) malloc ( sizeof(char)*MAX_STR) ;  

	if ( (st=open_file("/proc/sys/kernel/osrelease",&fd)) != OK ) { 
		return st ; 
	} 

	fgets(aux,MAX_STR,fd) ; 
	remove_newline(aux) ; 
	
	fprintf(stdout,"Kernel: %s \n",aux) ; 
	
	if ( fclose(fd) == EOF ) { 
		return ERR_CAN_NOT_CLOSE_FILE ; 
	} 

	free(aux) ; 

	return OK ; 
}

status_t print_uptime() { 

	status_t st ; 
	FILE * fd ; 
	char * aux ; 

	aux = (char*) malloc ( sizeof(char)*MAX_STR ) ; 
	
	if ( (st=open_file("/proc/uptime",&fd)) != OK ) { 
		return st ; 
	}

	fgets(aux,MAX_STR,fd) ; 
	parse_uptime(aux) ; 

	fprintf(stdout,"Uptime: %s \n",aux) ; 

	if (fclose(fd) == EOF) { 
		return ERR_CAN_NOT_CLOSE_FILE ; 
	}

	free(aux) ;
	return OK ; 	
}

status_t parse_uptime(char* str) { 
	double uptime ;

	get_uptime(&uptime,str) ;
	format_time(uptime,str) ; 
	
	return OK ; 
}

status_t get_uptime(double* uptime,char* str) {
	char * str_end_ptr  ;

	str_end_ptr = strpbrk(str," ") ; 
	*str_end_ptr = '\0' ; 
	
	*uptime = strtod(str,&str_end_ptr) ; 
	if ( *str_end_ptr != '\0'  ) {
		return ERR_CAN_NOT_PARSE_UPTIME ; 
	}

	return OK ;

}
	
		
void format_time(double time,char* str){
	int sec ; 
	int min ; 
	int hour ; 
	int day ;
	double sec_d ;
	
	sec = (int)(time/1) ; 
	min = sec/60 ; 
	hour = min/60 ; 
	day = hour/24 ; 
	
	sec = sec%60 ; 
	min = min%60 ; 
	hour = hour%24; 

	sec_d = sec + (time - (int)(time/1)) ; 
	sprintf(str,"%dD %d:%d:%.2f", day,hour,min,sec_d ) ; 

}
	
status_t print_num_fs() { 
	FILE* fd ; 
	int num_of_fs;
	status_t st ; 

	if ( (st = open_file("/proc/filesystems",&fd)) != OK ) { 
		return st ; 
	}

	num_of_fs = 0;
	while (!feof(fd) && !ferror(fd)) {
		if (getc(fd) == '\n') {
			++num_of_fs;
		}
	}
	printf("Number of FS: %d\n", num_of_fs) ; 

	if ( fclose(fd) == EOF ) { 
		return ERR_CAN_NOT_CLOSE_FILE ; 
	}

	return OK ; 
}

status_t print_host_and_time(void){ 
	FILE* fd ; 
	char* hostname ;
	char* time_str ; 
	time_t now ; 
	status_t st ;
	
	hostname = (char*) malloc (sizeof(char)*MAX_STR) ; 

	if ( (st=open_file("/etc/hostname",&fd)) != OK ) {
		return st ; 
	}

	fgets(hostname,MAX_STR,fd) ; 
	remove_newline(hostname) ; 

	if ( fclose(fd) == EOF ) { 
		return ERR_CAN_NOT_CLOSE_FILE ; 
	}

	now = time(NULL) ;
	time_str = ctime(&now) ; 
	fprintf(stdout,"%s\t\t%s",hostname,time_str) ; 

	free(hostname) ;

	return OK ;
}
	
status_t parse_cla(int argc,char* argv []) { 
	status_t st ;	

	if ( argc <= 1 ) { 
		return OK ; 
	} else { 
		if(!strcmp(argv[1],"-l")) {  
			if ((st=print_s())!=OK){ 
				return st ; 
			}
			if ((st=parse_l(argv[2],argv[3]))!= OK) { 
				return st ; 
			}
		} else if(!strcmp(argv[1],"-s")) { 
			if ((st=print_s())!=OK){ 
				return st ; 
			}  
		} 
	}
	return OK ; 	
}

status_t parse_l(char* interval_str, char*duration_str){ 
	char* endp ; 
	long interval ; 
	long duration ; 
	long iterations ; 
	status_t st ;

	interval = strtol (interval_str,&endp,DEC_BASE) ; 
	duration = strtol (duration_str,&endp,DEC_BASE) ; 
	
	iterations = duration/interval ; 

	while ( iterations>0 ) {
		if ((st = print_disk_requests()) != OK) { 
				return st ; 
		}  
		if ((st = print_available_and_total_memory()) != OK) { 
				return st ; 
		}
		if ((st = print_loadavg()) != OK ) { 
				return st ; 
		} 
		fprintf(stdout,"Waiting %ld seconds...\n",interval) ; 
		sleep(interval) ; 
		iterations--;
	}
	return OK ; 

}
	
status_t print_disk_requests() { 
	FILE * fd ; 
	char * aux ; 
	char * tokenp ; 
	char * endp ;
	int flag, i; 
	long reads ;
	long writes ;
	status_t st ;
	
	aux = (char*) malloc ( sizeof(char)*MAX_STR ) ; 
	if ( (st=open_file("/proc/diskstats",&fd)) != OK ) { 
		return st ; 
	}

	flag = 1 ; 

	while ( flag ) {  
		fgets(aux,MAX_STR,fd) ; 
		for (tokenp = strtok(aux," "); tokenp != NULL; tokenp = strtok(NULL, " "))
		{
			if(!strcmp(tokenp,"sda")) { 
				flag = 0 ; 
				break ;
			} 
		}
	}

	tokenp = strtok(NULL," \t") ; 
	reads = strtol(tokenp,&endp,DEC_BASE) ;  
	for ( i = 0 ; i < 4 ; i++ ) {
		tokenp=strtok(NULL,"\t ") ; 
	}

	writes = strtol(tokenp,&endp,DEC_BASE) ; 

	fprintf(stdout,"Disks request: %ld\n",reads+writes) ; 

	if ( fclose(fd) == EOF ) { 
		return ERR_CAN_NOT_CLOSE_FILE ; 
	}

	free (aux) ;
	
	return OK ; 
}

status_t print_available_and_total_memory() {
	FILE * fd ; 
	char * aux ; 
	char * endp ; 
	char * p ; 
	long total ; 
	long available ;
	status_t st ; 

	if ((st=open_file("/proc/meminfo",&fd))!=OK ){ 
		return st ; 
	}

	aux = (char*) malloc ( sizeof(char)*MAX_STR ) ; 

	fgets(aux,MAX_STR,fd) ; 
	parse_colon(aux,&p) ; 
	total = strtol(p,&endp,DEC_BASE) ; 

	fgets(aux,MAX_STR,fd) ; 
	parse_colon(aux,&p) ; 
	available = strtol(p,&endp,DEC_BASE) ; 

	fprintf(stdout,"Memory Available/Total: %ld/%ld\n",total,available ) ; 

	if ( fclose(fd) == EOF ) { 
		return ERR_CAN_NOT_CLOSE_FILE ; 
	}

	free (aux) ;

	return OK ; 	
} 
status_t print_loadavg() {
	FILE * fd ; 
	char* aux ;  
	char* endp ; 
	double loadavg ; 
	status_t st ; 

	if ((st=open_file("/proc/loadavg",&fd)) != OK ) { 
		return st ; 
	}

	aux = (char*) malloc (sizeof(char)*MAX_STR) ; 

	fgets(aux,MAX_STR,fd) ; 
	loadavg = strtod(aux,&endp) ; 
	
	fprintf(stdout,"Load average: %.2f\n",loadavg) ; 

	if ( fclose(fd) == EOF ) { 
		return ERR_CAN_NOT_CLOSE_FILE ; 
	}
	free(aux) ; 

	return OK ; 
} 	
status_t print_s () { 	
	FILE* fd ; 
	FILE* uptime_file ; 
	char* aux ; 
	char* endp ; 
	char * tokenp ; 
	double uptime ;
	time_t uptime_since_boot ; 
	time_t now ; 
	time_t boot_time ;
	int uptime_trunk ;
	long ctxt ;
	long processes ;
	status_t st ; 

	if ( (st=open_file("/proc/stat",&fd)) != OK ) { 
		return st ; 
	}
	
	aux = (char*) malloc (sizeof(char)*MAX_STR) ; 

	fgets(aux,MAX_STR,fd) ; 
	
	print_times(aux) ; 

	look_for("ctxt",aux,&tokenp,fd) ;
	ctxt = strtol(tokenp,&endp,DEC_BASE) ; 
	fprintf(stdout,"Context changes: %ld\n",ctxt) ; 

	look_for("processes",aux,&tokenp,fd) ;
	processes = strtol(tokenp,&endp,DEC_BASE) ; 
		
	fprintf(stdout,"Processes: %ld\n",processes) ; 
	if ((st=open_file("/proc/uptime",&uptime_file))!=OK ) {
		return st ; 
	}

	fgets(aux,MAX_STR,uptime_file) ; 
	get_uptime(&uptime,aux) ;
	uptime_trunk = (int) uptime ;	
	uptime_since_boot = (time_t) uptime_trunk ;
	now = time(NULL) ; 
	boot_time = now-uptime_since_boot ; 

	fprintf(stdout,"Boot time: %s",ctime(&boot_time) ) ; 

	if (fclose(fd)== EOF){ 
		return ERR_CAN_NOT_CLOSE_FILE ; 
	}

	if (fclose(uptime_file)== EOF){ 
		return ERR_CAN_NOT_CLOSE_FILE ; 
	}


	free(aux) ; 
		
	return OK ; 
}
		
void print_times(char* aux){ 
	char* user_str ;
	char* system_str ; 
	char* idle_str ;
	char* tokenp ; 
	char* endp ;
	long user ; 
	long idle ; 
	long system ;

	user_str = (char*) malloc (sizeof(char)*MAX_STR) ; 
	idle_str = (char*) malloc (sizeof(char)*MAX_STR) ; 
	system_str = (char*) malloc (sizeof(char)*MAX_STR) ; 

	tokenp = strtok(aux," ") ; 
	if ( !strcmp(tokenp,"cpu") ){ 
		tokenp = strtok(NULL," ") ; 
		user = strtol(tokenp,&endp,DEC_BASE) ;
	} 

	tokenp = strtok(NULL," ") ; 
	tokenp = strtok(NULL," ") ; 
	system = strtol(tokenp,&endp,DEC_BASE) ; 
	tokenp = strtok(NULL," ") ; 
	idle   = strtol(tokenp,&endp,DEC_BASE) ; 

	user = user/sysconf(_SC_CLK_TCK)  ; 
	system = system/sysconf(_SC_CLK_TCK) ; 
	idle = idle/sysconf(_SC_CLK_TCK) ;

	format_time(user,user_str) ;
	format_time(system,system_str) ; 
	format_time(idle,idle_str) ;

	fprintf(stdout,"UserTime: %s\nSystemTime: %s\nIdleTime: %s\n",user_str,system_str,idle_str) ; 

	free(user_str) ; 
	free(idle_str) ; 
	free(system_str) ;

}

void look_for(const char*str, char* line ,char** tokenp,FILE* fd){ 
	int	flag = 1 ;
	while(flag){ 
		fgets(line,MAX_STR,fd) ; 
		*tokenp = strtok(line," ") ; 
		if ( !strcmp(*tokenp,str) ) {
			*tokenp=strtok(NULL," ") ; 
			flag = 0 ;
		} 
	}
}
	
